#include "MyNotepad.h"

#include <QFileDialog>
#include <QIcon>
#include <QKeyEvent>
#include <QMessageBox>
#include <QStandardPaths>
#include <QString>
#include <QTextBlock>

namespace {}

MyNotepad::MyNotepad(QWidget* parent) : QMainWindow(parent), ui(new Ui_MyNotepad) {
    // 初始化MyNotepad类的成员变量
    ui->setupUi(this);
    pSearchAndReplace = new SearchAndReplace(this);
    pGoto = new Goto(this);
    currentFile = "";
    isSaved = true;
    // 初始化窗口名
    this->setWindowTitle("MyNotepad");
    // 初始化窗口的ico
    QIcon icon("mynotepad.ico");
    this->setWindowIcon(icon);
    // 初始化状态栏
    initStatusLabel();
    // 连接自定义的信号和槽函数
    connectAll();
}

MyNotepad::~MyNotepad() {
    delete ui;
    delete pSearchAndReplace;
    delete pGoto;
    unInitStatusLable();
}

void MyNotepad::connectAll() {
    // 链接信号与槽函数，其他槽函数自动链接
    connect(this->pSearchAndReplace, &SearchAndReplace::searchInfo, this, &MyNotepad::on_ReceiveSearchInfo);
    connect(this->pSearchAndReplace, &SearchAndReplace::replaceInfo, this, &MyNotepad::on_ReceiveReplaceInfo);
    connect(this->pGoto, &Goto::gotoLine, this, &MyNotepad::on_ReceiveGotoInfo);
}

void MyNotepad::initStatusLabel() {
    // 获取窗口的宽度
    int windows_width = this->width();
    // 创建四个状态栏控件，对有内容的控件指定宽度和对齐方式
    for (int i = 0; i < StatusLabelInfo::LABEL_NUM; i++) {
        statusLabel[i] = new QLabel();
        if (i) {
            statusLabel[i]->setFixedWidth(statusInfo.label_width[i]);
            statusLabel[i]->setAlignment(Qt::AlignLeft);
        }
        ui->statusbar->addWidget(statusLabel[i]);
    }
    statusLabel[StatusLabelInfo::VERSION]->setText("Version 1.0");
    // 更新光标位置，缩放比例，占位组件宽度
    updateStatusLabelCursor();
    updateStatusLabelScale();
    updateStatusLabelPlaceHolder();
}

void MyNotepad::unInitStatusLable() {
    for (int i = 0; i < StatusLabelInfo::LABEL_NUM; i++) {
        delete statusLabel[i];
    }
}

void MyNotepad::updateStatusLabelCursor() {
    auto cursor_pos = statusInfo.cursor_pos;
    statusLabel[StatusLabelInfo::CURSOR]->setText(
        QString("第%1行，第%2列").arg(cursor_pos.line).arg(cursor_pos.column));
}
void MyNotepad::updateStatusLabelScale() {
    statusLabel[StatusLabelInfo::SCALE]->setText(QString("%1%").arg(statusInfo.scale_ratio));
}
void MyNotepad::updateStatusLabelPlaceHolder() {
    auto begin = statusInfo.label_width.begin();
    auto end = statusInfo.label_width.end();
    int place_holder_width = this->width() - (std::accumulate(begin, end, 0));
    statusLabel[StatusLabelInfo::PLACEHOLDER]->setFixedWidth(place_holder_width);
}

void MyNotepad::updateWindowsTitle() {
    QString title = currentFile;
    // 确定窗口名
    if (currentFile.isEmpty()) {
        title = "新建文件.txt";
    } else {
        title = QFileInfo(currentFile).fileName();
    }
    // 确定保存标记
    if (!isSaved) {
        title += "*";
    }
    this->setWindowTitle(title);
}

void MyNotepad::on_NewAction_triggered() {
    qDebug() << "NewAction";
    checkSaved();
    ui->textEdit->clear();
    currentFile = "";
    isSaved = false;
    updateWindowsTitle();
}

void MyNotepad::on_OpenAction_triggered() {
    qDebug() << "OpenAction";
    checkSaved();
    // 获取桌面文件夹路径
    QString desktopPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
    // 打开文件对话框
    QString fileName = QFileDialog::getOpenFileName(this, "打开文件", desktopPath, "文本文件(*.txt)");
    qDebug() << fileName;
    if (fileName.isEmpty()) {
        return;
    } else {
        // 如果选取了文件，则打开文件
        QFile file(fileName);
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
            QMessageBox::information(this, "提示", "打开失败，文件可能被占用");
            return;
        }
        // 如果读取成功，则将文件内容显示到文本框中，并关联文件路径
        QTextStream in(&file);

        ui->textEdit->blockSignals(true);
        ui->textEdit->clear();
        ui->textEdit->setText(in.readAll());
        ui->textEdit->blockSignals(false);

        file.close();
        currentFile = fileName;
        // 窗口名更新为文件名
        isSaved = true;
        updateWindowsTitle();
    }
}

void MyNotepad::on_SaveAction_triggered() {
    qDebug() << "SaveAction";
    if (currentFile.isEmpty()) {  // 如果文件名为空，说明是新建文件，调用另存为
        on_SaveAsAction_triggered();
        return;
    } else {
        // 如果文件名不为空，说明是已经打开的文件，直接保存
        if (isSaved) {
            return;
        }
        outputToFile(currentFile);
    }
}

void MyNotepad::on_SaveAsAction_triggered() {
    qDebug() << "SaveAsAction";
    QString desktopPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
    QString fileName = QFileDialog::getSaveFileName(this, "另存为", desktopPath, "文本文件(*.txt)");
    if (fileName.isEmpty()) {
        return;
    } else {
        outputToFile(fileName, true);
    }
}

void MyNotepad::on_QuitAction_triggered() {
    qDebug() << "QuitAction";
    checkSaved();
    this->close();
}

void MyNotepad::on_textEdit_textChanged() {
    qDebug() << "textEdit_textChanged";
    isSaved = false;
    updateWindowsTitle();
}

void MyNotepad::on_textEdit_cursorPositionChanged() {
    qDebug() << "textEdit_cursorPositionChanged";
    QTextCursor currentCursor = ui->textEdit->textCursor();
    statusInfo.cursor_pos.line = currentCursor.blockNumber() + 1;
    statusInfo.cursor_pos.column = currentCursor.columnNumber() + 1;
    updateStatusLabelCursor();
}

void MyNotepad::on_StatusBarAction_triggered(bool checked) {
    qDebug() << "StatusBarAction_triggered" << checked;
    checked ? ui->statusbar->show() : ui->statusbar->hide();
}

void MyNotepad::on_ReplaceAction_triggered() { pSearchAndReplace->show(); }

void MyNotepad::on_FindAction_triggered() { pSearchAndReplace->show(); }

void MyNotepad::on_ReceiveSearchInfo(QString str, SearchDir dir) {
    qDebug() << "on_ReceiveSearchInfo" << str << (dir == SearchDir::LAST ? "LAST" : "NEXT");
    QTextCursor currentCursor = ui->textEdit->textCursor();
    // 当有选择状态时，指针在选中区域的尾部，因此需要偏移量
    int offset = currentCursor.hasSelection() ? str.length() : 0;
    int index = currentCursor.position();
    QString text = ui->textEdit->toPlainText();
    qDebug() << "text" << text << "index" << index;
    if (dir == SearchDir::LAST) {   // 向前查找
        index -= offset;
        if (index <= 0) {
            index = -1;
        } else {
            index = text.lastIndexOf(str, index - 1);
        }
    } else {                        // 向后查找
        index = text.indexOf(str, index);
    }
    if (index == -1) {              // 如果查找失败
        QMessageBox::information(this, "提示", "未找到");
    } else {
        // 如果成功，则修改光标位置并增加选中效果
        currentCursor.setPosition(index);
        currentCursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, str.length());
        ui->textEdit->setTextCursor(currentCursor);
    }
}

void MyNotepad::on_ReceiveReplaceInfo(QString sStr, QString rStr, ReplaceType type) {
    qDebug() << "on_ReceiveReplaceInfo" << sStr << " -> " << rStr << (type == ReplaceType::ONCE ? "ONCE" : "ALL");
    QTextCursor currentCursor = ui->textEdit->textCursor();
    int index = currentCursor.hasSelection() ? currentCursor.selectionStart() : currentCursor.position();
    QString text = ui->textEdit->toPlainText();
    // 从idx开始替换字符
    auto replaceStr = [&](int idx) {
        if (idx == -1)
            return;
        for (int i = 0; i < rStr.size(); i++) {
            text[idx + i] = rStr[i];
        }
    };
    if (type == ReplaceType::ONCE) {        // 单次替换
        index = text.indexOf(sStr, index);
        replaceStr(index);
    } else {                                // 全部替换
        index = 0;
        while (index != -1) {
            index = text.indexOf(sStr, index);
            replaceStr(index);
        }
    }
    ui->textEdit->setText(text);
}

void MyNotepad::on_ReceiveGotoInfo(int line) {
    qDebug() << "on_ReceiveGotoInfo" << line;
    QTextCursor currentCursor = ui->textEdit->textCursor();
    const auto& doc = ui->textEdit->document();
    int totalLIne = doc->lineCount();
    // 超出文本的行数则移动到最后一行
    line = std::min(line, totalLIne);
    int position = doc->findBlockByNumber(line - 1).position();
    currentCursor.setPosition(position);
    ui->textEdit->setTextCursor(currentCursor);
    // 跳转到指定行以后，关闭跳转窗口并将焦点这只在textEdit中
    pGoto->close();
    ui->textEdit->setFocus();
}

void MyNotepad::on_ZoomInAction_triggered() {
    // 限制到300%的最大缩放
    if (statusInfo.scale_ratio >= 300) {
        return;
    }
    statusInfo.scale_ratio += 10;
    ui->textEdit->zoomIn(1);
    updateStatusLabelScale();
}

void MyNotepad::on_ZoomOutAction_triggered() {
    // 限制到10%的最小缩放
    if (statusInfo.scale_ratio <= 10) {
        return;
    }
    statusInfo.scale_ratio -= 10;
    ui->textEdit->zoomOut(1);
    updateStatusLabelScale();
}

void MyNotepad::on_ZoomResetAction_triggered() {
    int zoomLevel = (statusInfo.scale_ratio - 100) / 10;
    // 根据当前的缩放反向缩放至默认缩放比例
    if (zoomLevel < 0) {
        ui->textEdit->zoomIn(-zoomLevel);
    } else {
        ui->textEdit->zoomOut(zoomLevel);
    }
    // 更新状态栏效果
    statusInfo.scale_ratio = 100;
    updateStatusLabelScale();
}

void MyNotepad::on_GotoAction_triggered() {
    qDebug() << "GotoAction_triggered";
    pGoto->show();
}

// 通过事件的方式检测键盘的触发
void MyNotepad::keyPressEvent(QKeyEvent* event) {
    qDebug() << "keyPressEvent" << event->key();
    if (event->modifiers() == Qt::ControlModifier) {
        switch (event->key()) {
            case Qt::Key_N:
                on_NewAction_triggered();
                break;
            case Qt::Key_O:
                on_OpenAction_triggered();
                break;
            case Qt::Key_S:
                on_SaveAction_triggered();
                break;
            case Qt::Key_F:
                on_FindAction_triggered();
                break;
            case Qt::Key_H:
                on_ReplaceAction_triggered();
                break;
            case Qt::Key_Equal:  // 直接按ctrl +
                on_ZoomInAction_triggered();
                break;
            case Qt::Key_Minus:
                on_ZoomOutAction_triggered();
                break;
            case Qt::Key_0:
                on_ZoomResetAction_triggered();
                break;
            case Qt::Key_G:
                on_GotoAction_triggered();
                break;
            default:
                break;
        }
    } else if (event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) {
        switch (event->key()) {
            case Qt::Key_Plus:  // 按下ctrl shift +
                on_ZoomInAction_triggered();
                break;
            case Qt::Key_S:
                on_SaveAsAction_triggered();
                break;
            default:
                break;
        }
    }
}

void MyNotepad::resizeEvent(QResizeEvent* event) { updateStatusLabelPlaceHolder(); }

void MyNotepad::closeEvent(QCloseEvent* event) {
    qDebug() << "closeEvent";
    checkSaved();
}

void MyNotepad::outputToFile(QString fileName, bool updCurrFile){
    QFile file(fileName);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QMessageBox::information(this, "提示", "保存失败，文件可能被占用");
        return;
    }
    QTextStream out(&file);
    out << ui->textEdit->toPlainText();
    file.close();
    if (updCurrFile){
        currentFile = fileName;
    }
    isSaved = true;
    updateWindowsTitle();
}

void MyNotepad::checkSaved() {
    qDebug() << "checkSaved";
    if (!isSaved) {                 // 如果未保存，则进行保存
        QMessageBox::StandardButton reply;
        reply = QMessageBox::warning(this, "未保存的更改", "当前文件未保存，是否保存？",
                                     QMessageBox::Yes | QMessageBox::No);
        if (reply == QMessageBox::Yes) {
            on_SaveAction_triggered();
        }
    }
}
// 逻辑梳理 ： 暂时不考虑当前窗口维护的文件状态，默认当前窗口为启动时的状态
// 窗口仅仅维护一个文件，需要记录当前文件名（完整路径），需要标记文件是否已经保存，同时维护窗口名
// 1. 新建文件
// 新建文件时，文件名为空，未保存，窗口名为“新建文件.txt*”
// 2. 打开文件
//     未选取文件，不做任何操作
//     选取文件
//         a.打开文件成功，文件名为打开的文件名，未修改，窗口名为文件名
//         b.打开文件失败，只弹窗提醒，不做其他任何操作
// 3. 保存文件
//     a.文件名为空，调用另存为
//     b.文件名不为空，
//          1.文件已经保存，不做任何操作
//          2.文件未保存，文件名不变，窗口名去掉*
// 4. 另存为
// 文件名更新为另存的文件名，状态更新为已保存，窗口名更新为文件名
// 5. 文本框内容改变

// 考虑当前窗口维护的文件状态，需要在新建、打开、退出时对未保存状态进行检查